#include "UtilityPCH.h"
#include "UT_SpectrumSampled.h"
#include "UT_MathHelper.h"
#include "UT_TPair.h"
#include "UT_SpectrumHelper.h"

#include "UT_SpectrumSampleData.c"

using namespace Utility;
//--------------------------------------------------------------------
UT_SpectrumSampled::UT_SpectrumSampled(float v)
{
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i)
		c[i] = v;
}
//--------------------------------------------------------------------
UT_SpectrumSampled::UT_SpectrumSampled(const UT_SpectrumBase<siSpecturmSampleCount> &v)
	: UT_SpectrumBase<siSpecturmSampleCount>(v)
{
	
}
//--------------------------------------------------------------------
UT_SpectrumSampled::UT_SpectrumSampled(const UT_SpectrumRGB &r, SpectrumType type /* = SPECTRUM_REFLECTANCE */)
{
	float rgb[3];
	r.ToRGB(rgb);
	*this = UT_SpectrumSampled::FromRGB(rgb, type);
}
//--------------------------------------------------------------------
void UT_SpectrumSampled::Init() 
{
	// Compute XYZ matching functions for _SampledSpectrum_
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i) 
	{
		float lambda0 = UT_MathHelper::Lerp(float(i) / float(siSpecturmSampleCount), siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
		float lambda1 = UT_MathHelper::Lerp(float(i+1) / float(siSpecturmSampleCount), siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
	
		X.c[i] = UT_SpectrumHelper::AverageSpectrumSamples(CIE_lambda, CIE_X, siCIESampleCount,lambda0, lambda1);
		Y.c[i] = UT_SpectrumHelper::AverageSpectrumSamples(CIE_lambda, CIE_Y, siCIESampleCount,lambda0, lambda1);
		Z.c[i] = UT_SpectrumHelper::AverageSpectrumSamples(CIE_lambda, CIE_Z, siCIESampleCount,lambda0, lambda1);
		yint += Y.c[i];
	}

	// Compute RGB to spectrum functions for _SampledSpectrum_
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i)
	{
		float lambda0 = UT_MathHelper::Lerp(float(i) / float(siSpecturmSampleCount),siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
		float lambda1 = UT_MathHelper::Lerp(float(i+1) / float(siSpecturmSampleCount),siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
		rgbRefl2SpectWhite.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectWhite,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectCyan.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectCyan,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectMagenta.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectMagenta,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectYellow.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectYellow,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectRed.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectRed,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectGreen.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectGreen,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbRefl2SpectBlue.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBRefl2SpectBlue,siRGB2SpectSampleCount, lambda0, lambda1);

		rgbIllum2SpectWhite.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectWhite,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectCyan.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectCyan,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectMagenta.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectMagenta,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectYellow.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectYellow,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectRed.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectRed,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectGreen.c[i]	= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectGreen,siRGB2SpectSampleCount, lambda0, lambda1);
		rgbIllum2SpectBlue.c[i]		= UT_SpectrumHelper::AverageSpectrumSamples(RGB2SpectLambda, RGBIllum2SpectBlue,siRGB2SpectSampleCount, lambda0, lambda1);
	}
}
//--------------------------------------------------------------------
UT_SpectrumSampled UT_SpectrumSampled::CreateSpectrumFromSamples(const float *lambda, const float *v, int32_t n)
{
	// Sort samples if unordered, use sorted for returned spectrum
	if (!UT_SpectrumHelper::SpectrumSamplesSorted(lambda, v, n)) 
	{
		UT_TVector<float> slambda(&lambda[0], &lambda[n]);
		UT_TVector<float> sv(&v[0], &v[n]);
		UT_SpectrumHelper::SortSpectrumSamples(&slambda.GetAt(0), &sv.GetAt(0), n);

		return CreateSpectrumFromSamples(&slambda.GetAt(0), &sv.GetAt(0), n);
	}

	UT_SpectrumSampled r;
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i)
	{
		// Compute average value of given SPD over $i$th sample's range
		float lambda0 = UT_MathHelper::Lerp(float(i) / float(siSpecturmSampleCount), siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
		float lambda1 = UT_MathHelper::Lerp(float(i+1) / float(siSpecturmSampleCount), siSpecturmSampleLambdaStart, siSpecturmSampleLambdaEnd);
		
		r.c[i] = UT_SpectrumHelper::AverageSpectrumSamples(lambda, v, n, lambda0, lambda1);
	}
	return r;
}
//--------------------------------------------------------------------
void UT_SpectrumSampled::ToXYZ(PARAM_OUT float xyz[3]) const
{
	xyz[0] = xyz[1] = xyz[2] = 0.f;
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i)
	{
		xyz[0] += X.c[i] * c[i];
		xyz[1] += Y.c[i] * c[i];
		xyz[2] += Z.c[i] * c[i];
	}
	xyz[0] /= yint;
	xyz[1] /= yint;
	xyz[2] /= yint;
}
//--------------------------------------------------------------------
float UT_SpectrumSampled::y() const
{
	float yy = 0.f;
	for (int32_t i = 0; i < siSpecturmSampleCount; ++i)
		yy += Y.c[i] * c[i];
	return yy / yint;
}
//--------------------------------------------------------------------
void UT_SpectrumSampled::ToRGB(PARAM_OUT float rgb[3]) const
{
	float xyz[3];
	ToXYZ(xyz);
	UT_SpectrumHelper::XYZToRGB(xyz, rgb);
}
//--------------------------------------------------------------------
UT_SpectrumRGB UT_SpectrumSampled::ToRGBSpectrum() const
{
	float rgb[3];
	ToRGB(rgb);
	return UT_SpectrumRGB::FromRGB(rgb);
}
//--------------------------------------------------------------------
UT_SpectrumSampled UT_SpectrumSampled::FromRGB(const float rgb[3], SpectrumType type /* = SPECTRUM_REFLECTANCE */)
{
	UT_SpectrumSampled r;
	if (type == SPECTRUM_REFLECTANCE)
	{
		// Convert reflectance spectrum to RGB
		if (rgb[0] <= rgb[1] && rgb[0] <= rgb[2])
		{
			// Compute reflectance _SampledSpectrum_ with _rgb[0]_ as minimum
			r += rgb[0] * rgbRefl2SpectWhite;
			if (rgb[1] <= rgb[2])
			{
				r += (rgb[1] - rgb[0]) * rgbRefl2SpectCyan;
				r += (rgb[2] - rgb[1]) * rgbRefl2SpectBlue;
			}
			else
			{
				r += (rgb[2] - rgb[0]) * rgbRefl2SpectCyan;
				r += (rgb[1] - rgb[2]) * rgbRefl2SpectGreen;
			}
		}
		else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2])
		{
			// Compute reflectance _SampledSpectrum_ with _rgb[1]_ as minimum
			r += rgb[1] * rgbRefl2SpectWhite;
			if (rgb[0] <= rgb[2]) {
				r += (rgb[0] - rgb[1]) * rgbRefl2SpectMagenta;
				r += (rgb[2] - rgb[0]) * rgbRefl2SpectBlue;
			}
			else {
				r += (rgb[2] - rgb[1]) * rgbRefl2SpectMagenta;
				r += (rgb[0] - rgb[2]) * rgbRefl2SpectRed;
			}
		}
		else
		{
			// Compute reflectance _SampledSpectrum_ with _rgb[2]_ as minimum
			r += rgb[2] * rgbRefl2SpectWhite;
			if (rgb[0] <= rgb[1]) {
				r += (rgb[0] - rgb[2]) * rgbRefl2SpectYellow;
				r += (rgb[1] - rgb[0]) * rgbRefl2SpectGreen;
			}
			else {
				r += (rgb[1] - rgb[2]) * rgbRefl2SpectYellow;
				r += (rgb[0] - rgb[1]) * rgbRefl2SpectRed;
			}
		}
		r *= .94;
	}
	else {
		// Convert illuminant spectrum to RGB
		if (rgb[0] <= rgb[1] && rgb[0] <= rgb[2]) {
			// Compute illuminant _SampledSpectrum_ with _rgb[0]_ as minimum
			r += rgb[0] * rgbIllum2SpectWhite;
			if (rgb[1] <= rgb[2]) {
				r += (rgb[1] - rgb[0]) * rgbIllum2SpectCyan;
				r += (rgb[2] - rgb[1]) * rgbIllum2SpectBlue;
			}
			else {
				r += (rgb[2] - rgb[0]) * rgbIllum2SpectCyan;
				r += (rgb[1] - rgb[2]) * rgbIllum2SpectGreen;
			}
		}
		else if (rgb[1] <= rgb[0] && rgb[1] <= rgb[2]) {
			// Compute illuminant _SampledSpectrum_ with _rgb[1]_ as minimum
			r += rgb[1] * rgbIllum2SpectWhite;
			if (rgb[0] <= rgb[2]) {
				r += (rgb[0] - rgb[1]) * rgbIllum2SpectMagenta;
				r += (rgb[2] - rgb[0]) * rgbIllum2SpectBlue;
			}
			else {
				r += (rgb[2] - rgb[1]) * rgbIllum2SpectMagenta;
				r += (rgb[0] - rgb[2]) * rgbIllum2SpectRed;
			}
		}
		else {
			// Compute illuminant _SampledSpectrum_ with _rgb[2]_ as minimum
			r += rgb[2] * rgbIllum2SpectWhite;
			if (rgb[0] <= rgb[1]) {
				r += (rgb[0] - rgb[2]) * rgbIllum2SpectYellow;
				r += (rgb[1] - rgb[0]) * rgbIllum2SpectGreen;
			}
			else {
				r += (rgb[1] - rgb[2]) * rgbIllum2SpectYellow;
				r += (rgb[0] - rgb[1]) * rgbIllum2SpectRed;
			}
		}
		r *= .86445f;
	}
	return r.Clamp();
}
//--------------------------------------------------------------------
UT_SpectrumSampled UT_SpectrumSampled::FromXYZ(const float xyz[3], SpectrumType type /* = SPECTRUM_REFLECTANCE */)
{
	float rgb[3];
	UT_SpectrumHelper::XYZToRGB(xyz, rgb);
	return FromRGB(rgb, type);
}
//--------------------------------------------------------------------

//--------------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------
//--------------------------------------------------------------------